#include "epd_9in7_bw.h"

#if (EPD_DRIVER_TYPE == EPD_DRIVER_9IN7_BW)

#define EPD_9IN7_BW_WIDTH   EPD_WIDTH
#define EPD_9IN7_BW_HEIGHT  EPD_HEIGHT

static uint8_t epd_reinited = 0;

static void epd_9in7_bw_init(void);
static void epd_9in7_bw_refresh(epd_refresh_t type);
static void epd_9in7_bw_select_ram(epd_ram_t type);
static void epd_9in7_bw_select_window(uint16_t x_start, uint16_t y_start, uint16_t width, uint16_t *height);
static void epd_9in7_bw_select_cursor(uint16_t x_start, uint16_t y_start);
static void epd_9in7_bw_send_data_block(uint8_t *data, uint32_t len);
static int8_t epd_9in7_bw_read_temp(void);


epd_driver_t epd_9in7_bw = {
    .init = epd_9in7_bw_init,
    .refresh = epd_9in7_bw_refresh,
    .select_ram = epd_9in7_bw_select_ram,
    .select_window = epd_9in7_bw_select_window,
    .select_cursor = epd_9in7_bw_select_cursor,
    .send_data_block = epd_9in7_bw_send_data_block,
    .read_temp = epd_9in7_bw_read_temp,
};


static void epd_9in7_bw_send_command(uint8_t command) {
    EPD_CRITICAL_SECTION(
        epd_pin_write(EPD_PIN_CS, EPD_GPIO_HIGH); // CS1
        epd_pin_write(EPD_PIN_CS, EPD_GPIO_LOW);  // CS0
        epd_pin_write(EPD_PIN_DC, EPD_GPIO_LOW);  // DC0
        epd_delay_ms(1);
        epd_spi_write_byte(command);
        epd_delay_ms(1);
        epd_pin_write(EPD_PIN_CS, EPD_GPIO_HIGH); // CS1
    );
}

static void epd_9in7_bw_send_data(uint8_t data) {
    EPD_CRITICAL_SECTION(
        epd_pin_write(EPD_PIN_CS, EPD_GPIO_HIGH); // CS1
        epd_pin_write(EPD_PIN_CS, EPD_GPIO_LOW);  // CS0
        epd_pin_write(EPD_PIN_DC, EPD_GPIO_HIGH); // DC1
        epd_delay_ms(1);
        epd_spi_write_byte(data);
        epd_delay_ms(1);
        epd_pin_write(EPD_PIN_CS, EPD_GPIO_HIGH); // CS1
    );
}

static void epd_9in7_bw_send_data_block(uint8_t *data, uint32_t len) {
    // TODO Si917中临界区内无法判断是否发送完成，暂时这么写
    //EPD_CRITICAL_SECTION(
    epd_pin_write(EPD_PIN_CS, EPD_GPIO_HIGH); // CS1
    epd_pin_write(EPD_PIN_CS, EPD_GPIO_LOW);  // CS0
    epd_pin_write(EPD_PIN_DC, EPD_GPIO_HIGH); // DC1
    epd_delay_ms(1);
    epd_spi_write_buffer(data, len);
    epd_delay_ms(1);
    epd_pin_write(EPD_PIN_CS, EPD_GPIO_HIGH); // CS1
    //);
}


static uint8_t epd_9in7_bw_read_data(void) {
    uint8_t data = 0;
    EPD_CRITICAL_SECTION(
        epd_pin_write(EPD_PIN_CS, EPD_GPIO_HIGH); // CS1
        epd_pin_write(EPD_PIN_CS, EPD_GPIO_LOW);  // CS0
        epd_pin_write(EPD_PIN_DC, EPD_GPIO_HIGH); // DC1
        epd_delay_ms(1);
        data = epd_spi_read_byte();
        epd_delay_ms(1);
        epd_pin_write(EPD_PIN_CS, EPD_GPIO_HIGH); // CS1
    );
    return data;
}


static void epd_9in7_bw_reset(void) {
    if(epd_reinited) {
        return;
    }
    epd_reinited = 1;
    epd_pin_write(EPD_PIN_RST, EPD_GPIO_LOW);
    epd_delay_ms(10);
    epd_pin_write(EPD_PIN_RST, EPD_GPIO_HIGH);
    epd_delay_ms(10);
}

static void epd_9in7_bw_sleep(void) {
    epd_9in7_bw_send_command(0x10);
    epd_9in7_bw_send_data(0x01);    // 进入睡眠模式1
    epd_delay_ms(10);
    epd_reinited = 0;
}

static void epd_9in7_bw_read_busy(void) {
    uint16_t busy_count = 0;
    while(epd_pin_read(EPD_PIN_BUSY) == EPD_GPIO_HIGH) {
        epd_delay_ms(2);
        busy_count += 2;
        if(busy_count > 30000) { //30s超时
            break;
        }
    }
}

static void epd_9in7_bw_init(void) {
    epd_io_init();

    epd_9in7_bw_reset();
    epd_9in7_bw_read_busy();

    epd_9in7_bw_send_command(0x12);
    epd_9in7_bw_read_busy();

    epd_9in7_bw_send_command(0x0C);  // Set Driver Output Control for Gate setting = Gate Channel - 1
    epd_9in7_bw_send_data(0xAE);     // For example, for 300 gate channel => 300 - 1 = 299 (0x12B)
    epd_9in7_bw_send_data(0xC7);
    epd_9in7_bw_send_data(0xC3);
    epd_9in7_bw_send_data(0xC0);
    epd_9in7_bw_send_data(0x80);

    epd_9in7_bw_send_command(0x01);  // Set Driver Output Control for Gate setting = Gate Channel - 1
    epd_9in7_bw_send_data(0x9F);     // For example, for 300 gate channel => 300 - 1 = 299 (0x12B)
    epd_9in7_bw_send_data(0x02);
    epd_9in7_bw_send_data(0x00);

    epd_9in7_bw_send_command(0x11);  // Set Data Entry Mode setting
    epd_9in7_bw_send_data(0x03);     // Y increment, X increment, address counter in X-direction

    epd_9in7_bw_send_command(0x3C);  // board
    epd_9in7_bw_send_data(0x80);     // White: 0x01

    epd_9in7_bw_send_command(0x18);  // Select internal temperature sensor
    epd_9in7_bw_send_data(0x80);

    epd_9in7_bw_read_busy();
}

static void epd_9in7_bw_select_window(uint16_t x_start, uint16_t y_start, uint16_t width, uint16_t *height)
{
    uint32_t x_end = x_start + width;
    uint32_t y_end = 0;
    if(y_start + *height > EPD_9IN7_BW_HEIGHT) {
        *height = EPD_9IN7_BW_HEIGHT - y_start;
    }
    y_end = y_start + *height - 1;
    x_start = (x_start % 8 == 0) ? (x_start) : (x_start / 8 * 8 + 8);
    x_end = (x_end % 8 == 0) ? (x_end) : (x_end / 8 * 8);
    x_end = x_end - 1;

    epd_9in7_bw_reset();
    epd_9in7_bw_read_busy();

    epd_9in7_bw_send_command(0x44);
    epd_9in7_bw_send_data(x_start & 0xFF);
    epd_9in7_bw_send_data((x_start >> 8) & 0xFF);
    epd_9in7_bw_send_data(x_end & 0xFF);
    epd_9in7_bw_send_data((x_end >> 8) & 0xFF);

    epd_9in7_bw_send_command(0x45);
    epd_9in7_bw_send_data(y_start & 0xFF);
    epd_9in7_bw_send_data((y_start >> 8) & 0xFF);
    epd_9in7_bw_send_data(y_end & 0xFF);
    epd_9in7_bw_send_data((y_end >> 8) & 0xFF);
}

static void epd_9in7_bw_select_cursor(uint16_t x_start, uint16_t y_start)
{
    x_start = (x_start % 8 == 0) ? (x_start) : (x_start / 8 * 8 + 8);

    epd_9in7_bw_send_command(0x4E);
    epd_9in7_bw_send_data(x_start & 0xFF);
    epd_9in7_bw_send_data((x_start >> 8) & 0xFF);

    epd_9in7_bw_send_command(0x4F);
    epd_9in7_bw_send_data(y_start & 0xFF);
    epd_9in7_bw_send_data((y_start >> 8) & 0xFF);
}

static void epd_9in7_bw_select_ram(epd_ram_t type) {
    if(type == EPD_CUR_RAM) {
        epd_9in7_bw_send_command(0x24);
    } else {
        epd_9in7_bw_send_command(0x26);
    }
    epd_9in7_bw_read_busy();
}

static void epd_9in7_bw_refresh(epd_refresh_t type) {
    epd_9in7_bw_send_command(0x22);
    if(type == EPD_REFRESH_FULL) {
        epd_9in7_bw_send_data(0xF7); // 全刷
    } else {
        epd_9in7_bw_send_data(0xFF); // 局刷
    }
    epd_9in7_bw_send_command(0x20);
    epd_9in7_bw_read_busy();
    epd_9in7_bw_sleep();
}

static int8_t epd_9in7_bw_read_temp(void) {
    uint8_t data[2] = {0};
    int8_t temp_value = 0;
    epd_9in7_bw_reset();
    epd_9in7_bw_read_busy();

    epd_9in7_bw_send_command(0x18);
    epd_9in7_bw_send_data(0x80);
    epd_9in7_bw_send_command(0x22);
    epd_9in7_bw_send_data(0xB1);
    epd_9in7_bw_send_command(0x20);
    epd_9in7_bw_read_busy();

    epd_9in7_bw_send_command(0x1B);
    data[0] = epd_9in7_bw_read_data();
    data[1] = epd_9in7_bw_read_data();
    temp_value = data[0];
    epd_9in7_bw_read_busy();

    epd_delay_ms(5);
    // EPD_9IN7_HINK_Sleep();
    return temp_value;
}

#endif // EPD_DRIVER_TYPE == EPD_DRIVER_9IN7_BW